package com.winit.core.ci.vcs;

import com.winit.common.util.ConfigUtil;
import com.winit.core.ci.vcs.exception.VCSException;
import com.winit.core.ci.vcs.handler.ConflictResolverHandler;
import com.winit.core.ci.vcs.handler.SvnVersionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.*;

import java.io.File;
import java.io.FileNotFoundException;

/**
 * Created by wangjian on 16/8/1
 *
 * 所有方法返回map
 * code:0 成功    1 失败
 * desc:描述
 *
 */
public class SVN implements VCS {

    /**
     * svn地址
     */
    private String svnUrl;
    /**
     * svn登陆用户名
     */
    private String svnUserName;
    /**
     * svn密码
     */
    private String svnUserPass;
    /**
     * 本地svn目录或文件路径
     * 检出文件时为目录
     * 更新/提交时可为路径
     */
    private String path;

    private SvnVersionHandler handler = null;

    public SVN(String svnUrl, String svnUserName, String svnUserPass, String path){
        this.svnUrl = svnUrl;
        this.svnUserName = svnUserName;
        this.svnUserPass = svnUserPass;
        this.path = path;
    }

    public SVN(String svnUrl, String path){
        this.svnUrl = svnUrl;
        this.path = path;
        this.svnUserName = ConfigUtil.getValue("svn_user_name");
        this.svnUserPass = ConfigUtil.getValue("svn_user_pass");
    }

    public SVN(String path){
        this.path = path;
        this.svnUserName = ConfigUtil.getValue("svn_user_name");
        this.svnUserPass = ConfigUtil.getValue("svn_user_pass");
    }

    public boolean doCheckOut() throws VCSException{
        SvnUtil svnUtil = new SvnUtil();
        svnUtil.setHandler(this.handler);
        return svnUtil.doCheckOut(this.svnUserName,this.svnUserPass,this.svnUrl,this.path);
    }

    public boolean doExport() throws VCSException{
        SvnUtil svnUtil = new SvnUtil();
        svnUtil.setHandler(this.handler);
        return svnUtil.doExport(this.svnUserName,this.svnUserPass,this.svnUrl,this.path);
    }

    public boolean doUpdate() throws VCSException{
        SvnUtil svnUtil = new SvnUtil();
        svnUtil.setHandler(this.handler);
        return svnUtil.doUpdate(this.svnUserName,this.svnUserPass,this.path);
    }

    public boolean doCommit() throws VCSException{
        SvnUtil svnUtil = new SvnUtil();
        svnUtil.setHandler(this.handler);
        return svnUtil.doCommit(this.svnUserName,this.svnUserPass,this.path);
    }

    public boolean doAdd() throws VCSException{
        SvnUtil svnUtil = new SvnUtil();
        svnUtil.setHandler(this.handler);
        return svnUtil.doAdd(this.svnUserName,this.svnUserPass,this.path);
    }

    public void setHandler(SvnVersionHandler handler) {
        this.handler = handler;
    }

    private class SvnUtil{

        private SVNClientManager mySVNClientManager;

        private SvnVersionHandler handler = null;

        private final Logger logger = LoggerFactory.getLogger(SvnUtil.class);

        public boolean doCheckOut(String userName, String passWord, String svnUrl, String workDir) throws VCSException{
            logger.info("checkOut:检出svn代码,svnUrl="+svnUrl+",workDir="+workDir);
            validate(userName,passWord,svnUrl,workDir,true);
            try {
                init(userName,passWord);
                checkOut(svnUrl, workDir);
                return true;
            }catch (Exception e){
                throw new VCSException(e,svnUrl);
            }
        }

        public boolean doExport(String userName, String passWord, String svnUrl, String workDir) throws VCSException{
            logger.info("export:检出svn代码,svnUrl="+svnUrl+",workDir="+workDir);
            validate(userName,passWord,svnUrl,workDir,true);
            try {
                init(userName,passWord);
                export(svnUrl, workDir);
                return true;
            }catch (Exception e){
                throw new VCSException(e,svnUrl);
            }
        }

        public boolean doUpdate(String userName, String passWord,String workDir) throws VCSException{
            logger.info("update:更新代码,workDir="+workDir);
            validate(userName,passWord,"",workDir,false);
            try {
                init(userName,passWord);
                cleanup(workDir);
                update(workDir);
                return true;
            }catch (Exception e){
                throw new VCSException(e,workDir);
            }
        }

        public boolean doCommit(String userName, String passWord,String filePath) throws VCSException{
            logger.info("commit:提交代码,filePath="+filePath);
            validate(userName,passWord,"",filePath,false);
            try {
                init(userName,passWord);
                update(filePath);
                commit(filePath);
                return true;
            }catch (Exception e){
                throw new VCSException(e,filePath);
            }
        }

        public boolean doAdd(String userName, String passWord,String filePath) throws VCSException{
            logger.info("add:添加到本地仓库,filePath="+filePath);
            validate(userName,passWord,"",filePath,false);
            try {
                init(userName,passWord);
                add(filePath);
                return true;
            }catch (Exception e){
                throw new VCSException(e,filePath);
            }
        }

        private void validate(String userName, String passWord, String svnUrl, String workDir,boolean isUseUrl) throws VCSException{
            if(isUseUrl && StringUtils.isEmpty(svnUrl)){
                throw new VCSException("svn地址不能为空,请检查svn地址是否正确!");
            }
            if(StringUtils.isEmpty(userName)){
                throw new VCSException("svn用户名不能为空,请联系beetle管理员!");
            }
            if(StringUtils.isEmpty(passWord)){
                throw new VCSException("svn密码不能为空,请联系beetle管理员!");
            }
            if(StringUtils.isEmpty(workDir)){
                throw new VCSException("svn文件本地路径不能为空,请联系beetle管理员!");
            }
        }

        private void init(String userName,String passWord) throws Exception{
            //支持http:// 和 https://
            DAVRepositoryFactory.setup();
            //支持svn://和svn+ssh://
            SVNRepositoryFactoryImpl.setup();
            //支持file:///
            FSRepositoryFactory.setup();
            ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
            mySVNClientManager = SVNClientManager.newInstance((DefaultSVNOptions) options,userName,passWord);
        }

        //检出代码,包含.svn文件
        private void checkOut(String svnUrl,String workDir) throws SVNException {
            SVNURL svnurl = SVNURL.parseURIEncoded(svnUrl);
            SVNUpdateClient svnUpdateClient = mySVNClientManager.getUpdateClient();
            svnUpdateClient.setIgnoreExternals( false );

            long version = svnUpdateClient.doCheckout(svnurl, new File(workDir), SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY,true);
            logger.info("checkOut:检出svn代码,svnUrl="+svnUrl+",workDir="+workDir+",当前副本版本号:"+version);
            this.sendVersion(svnUrl,workDir,version);
        }
        //检出代码,不包含.svn文件
        private void export(String svnUrl,String workDir) throws SVNException{
            SVNURL svnurl = SVNURL.parseURIEncoded(svnUrl);
            SVNUpdateClient svnUpdateClient = mySVNClientManager.getUpdateClient();
            svnUpdateClient.setIgnoreExternals( false );

            long version = svnUpdateClient.doExport(svnurl,new File(workDir),SVNRevision.HEAD,SVNRevision.HEAD,null,false,SVNDepth.INFINITY);
            logger.info("export:检出svn代码,svnUrl="+svnUrl+",workDir="+workDir+",当前副本版本号:"+version);
            this.sendVersion(svnUrl,workDir,version);
        }
        //更新代码,以本地代码为准
        private void update(String workDir) throws SVNException{
            SVNUpdateClient svnUpdateClient = mySVNClientManager.getUpdateClient();
            svnUpdateClient.setIgnoreExternals( false );

            DefaultSVNOptions op = (DefaultSVNOptions) svnUpdateClient.getOptions();
            op.setConflictHandler(new ConflictResolverHandler());

            long version = svnUpdateClient.doUpdate(new File(workDir),SVNRevision.HEAD,SVNDepth.INFINITY,false,false);
            logger.info("update:更新代码,workDir="+workDir+",当前副本版本号:"+version);
            this.sendVersion("",workDir,version);
        }
        //清楚锁定
        private void cleanup(String workDir) throws SVNException{
            SVNWCClient svnwcClient = mySVNClientManager.getWCClient();
            svnwcClient.setIgnoreExternals(false);
            File file = new File(workDir);
            if(!file.isDirectory()){
                svnwcClient.doCleanup(file.getParentFile());
            }else {
                svnwcClient.doCleanup(file);
            }
        }

        //提交代码
        private void commit(String filePath) throws Exception{
            File commitFile = new File(filePath);
            logger.info("commit:提交代码,filePath="+filePath);
            if(!commitFile.exists()){
                throw new FileNotFoundException("文件不存在:"+filePath);
            }

            SVNStatus svnStatus = mySVNClientManager.getStatusClient().doStatus(commitFile,false);
            logger.info("commit:提交代码,检查文件状态,status:"+svnStatus.getCombinedNodeAndContentsStatus());
            if(SVNStatusType.STATUS_UNVERSIONED == svnStatus.getCombinedNodeAndContentsStatus()){
                logger.info("commit:提交代码,添加文件到本地版本库,filePath="+filePath);
                mySVNClientManager.getWCClient().doAdd(commitFile,true,false,false,SVNDepth.INFINITY,false,false);
            }
            SVNCommitInfo svnCommitInfo = mySVNClientManager.getCommitClient().doCommit(new File[]{commitFile}, true, "test", null, null, true, false, SVNDepth.INFINITY);
            logger.info("commit:提交代码,svnCommitInfo="+svnCommitInfo.toString());
        }

        private void add(String filePath) throws SVNException{
            File commitFile = new File(filePath);
            logger.info("add:添加到本地仓库,filePath="+filePath);
            mySVNClientManager.getWCClient().doAdd(commitFile,true,false,false,SVNDepth.INFINITY,false,false);
        }

        private void sendVersion(String svnUrl,String workDir,long version){
            if(this.handler == null){
                return;
            }
            this.handler.onSvnVersion(svnUrl,workDir,version);
        }

        public void setHandler(SvnVersionHandler handler) {
            this.handler = handler;
        }
    }
}
